﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoRel;

namespace ExampleImplementation.NewFolder1
{
    public class ColumnAlias {
        private ColumnReference m_Reference;
        private String m_Alias;
        private int m_TableIndex;

        private PropertyProxy m_Proxy;

        public PropertyProxy Proxy { get { return m_Proxy; } }
        public ColumnReference Reference { get { return m_Reference; } }
        public string Alias { get { return m_Alias; } }
        public int TableIndex { get { return m_TableIndex; } }
    }

    public class Join {
        public class TableAlias {
            private String m_TableAlias;
            private Table m_Table;
            private ColumnSet m_JoinCondition;
            private String m_DependantOn;

            public string TableAlias1 { get { return m_TableAlias; } }
            public Table Table { get { return m_Table; } }
            public ColumnSet JoinCondition { get { return m_JoinCondition; } }
            public String DependantOn { get { return m_DependantOn; } }

            private List< TableAlias > m_Dependants;
            public List< TableAlias > Dependants { get { return m_Dependants; } }
        }

        private List< TableAlias > m_Tables;
        private List< ColumnAlias > m_Columns;

        private Dictionary< TableAlias, List< TableAlias > > m_JoinDependencies;

        public enum JoinType{ Left, Inner, Full }
        public void DeclareTable( String table_alias, Table table, ColumnSet join_condition, JoinType join_type ){}
        public void DeclareColumnAlias( String table_alias, String column_alias, String underlying_column ){}

        public void InitializeEvents() {
            foreach( TableAlias table_alias in m_Tables ) {

                if( String.IsNullOrEmpty( table_alias.DependantOn ) ) {
                    TableAlias item = table_alias;
                    table_alias.Table.RegisterListener( null,
                        delegate(Key value, TableChangeArgs args)
                        {
                            HandleTableChange(item, value, args);
                        });
                } else {
                    
                }
            }
        }

        private void HandleTableChange( TableAlias table_alias, Key value, TableChangeArgs args ) {
            switch( args.ChangeType ) {
                case ChangeType.Add:
                    HandleAdd( table_alias, value, args );
                    break;
                case ChangeType.Remove:
                case ChangeType.Update:
                case ChangeType.Clear:
                    throw new NotImplementedException();
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private JoinRow GetOrCreateJoinRow( TableAlias table_alias, Key value ){ throw new NotImplementedException(); }

        private void HandleAdd( TableAlias table_alias, Key value, TableChangeArgs args ) {
            JoinRow row = GetOrCreateJoinRow( table_alias, value );
            
        }
    }

    public class CombinedKey {
        public Key GetKeyPortion( int index ){ throw new NotImplementedException();}
    }

    public class Subset {
        private readonly List< Key > m_JoinKey;
        private readonly List< IndexBucket > m_Buckets;
        private readonly List< Index > m_Indices;
        private Dictionary< CombinedKey, JoinRow > m_Rows; 
        
        public Subset( List<Key> join_keys, List<Index> indices ) { 
            m_Indices = indices;
            m_JoinKey = join_keys;
            m_Buckets = new List<IndexBucket>();
            m_Rows = new Dictionary<CombinedKey, JoinRow>();

            for( int i=0 ; i < m_Indices.Count ; i++ ) {
                IndexBucket bucket;
                if (m_Indices[i].TryGetBucket(m_JoinKey[i], out bucket) == false ) {
                    throw new NotImplementedException();
                }
                m_Buckets.Add( bucket );
            }
        }

        public void Add(CombinedKey key, JoinRow row) { m_Rows.Add(key, row); }
        public void Remove(CombinedKey key, JoinRow row) { m_Rows.Remove(key); }
    }

    public class JoinBucket {
        private readonly List< Key > m_JoinKeys; 
        private readonly List< IndexBucket > m_Buckets;
        private readonly List< JoinRow > m_OutputRows;
        private readonly Dictionary< CombinedKey, JoinRow > m_UniqueRows;
        private readonly List< Subset > m_Subsets;

        

        public JoinBucket( List< IndexBucket > buckets ) {
            m_Buckets = buckets;
            for (int i = 0; i < m_Buckets.Count; i++ ) {
                List<Key> join_keys = new List< Key >();
                List<IndexBucket> indices = new List< IndexBucket >();
                for( int j = 0 ; j < m_Buckets.Count ; j ++ ) {
                    if( j == i ) continue;
                    join_keys.Add( m_JoinKeys[j]  );
                    indices.Add( m_Buckets[j]  );
                }
                m_Subsets[i] = new Subset(join_keys, indices);
            }

            for (int i = 0; i < buckets.Count; i++)
                {
                    int i1 = i;
                    m_Buckets[i].ChangePending += delegate(Key value, TableChangeArgs args)
                    {
                        HandleBucketChange(i1, value, args);
                    };
                }
        }

        private CombinedKey GetCombinedKeyViaReplacement( int bucket_index, Key item, JoinRow row ) { throw new NotImplementedException(); }
        private void HandleBucketChange( int bucket_index, Key value, TableChangeArgs args ) {
            TableChangeItem item = null;

            // All of this logic is pretty fugly. Like, wow, fugly and likely incorrect.
            // but this logic is sort of oddly different, or I'm oddly retarded.
            if( m_UniqueRows.Count == 0 ) {
                JoinRow row = new JoinRow( item );
                m_UniqueRows.Add( new CombinedKey(), row  );
                return;
            }

            if( args.ChangeType == ChangeType.Add ) {
                Subset subset = m_Subsets[ bucket_index ];
                List< JoinRow > addition_rows = new List< JoinRow >();

                foreach( KeyValuePair< CombinedKey, JoinRow > pair in subset.Items ) {
                    // create a combinedkey that inserts our items unique key into the key list.
                    CombinedKey resultant_key = new CombinedKey( pair.Key, bucket_index, item.NewValue.Key );
                    JoinRow row = new JoinRow( resultant_key, pair.Value, item.NewValue );
                    addition_rows.Add( row );
                }

                foreach( JoinRow row in addition_rows ) {
                    // this should handle notification of subsets
                    AddRow( bucket_index, value, row );
                }

            } else if( args.ChangeType == ChangeType.Remove ) {
                Subset subset = m_Subsets[bucket_index];
                List<CombinedKey> removal_keys = new List<CombinedKey>();

                foreach (KeyValuePair<CombinedKey, JoinRow> pair in subset.Items)
                {
                    // create a combinedkey that inserts our items unique key into the key list.
                    CombinedKey resultant_key = new CombinedKey(pair.Key, bucket_index, item.NewValue.Key);
                    removal_keys.Add( resultant_key );
                }
                // remove may get reinterpreted as an update
                foreach( CombinedKey key in removal_keys ) {
                    JoinRow row = m_UniqueRows[ key ];
                    UpdateResult result = row.Update( key, bucket_index, null, item.OldValue );
                    switch( result ) {
                        case UpdateResult.Remove:
                            RemoveRow( row, bucket_index );
                            break;
                        case UpdateResult.Update:
                            UpdateRow( row, key, bucket_index, null, item.OldValue );
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            } else if( args.ChangeType == ChangeType.Update ) {
                foreach (JoinRow row in m_UniqueRows.Values){
                    if (row.Key == GetCombinedKeyViaReplacement(bucket_index, item.OldValue.Key, row)) {
                        row.Update( item.NewValue, item.OldValue );
                    }
                }
            }
        }
    }

    public enum UpdateResult {
        Remove,
        Update
    }
    public class JoinRow {
        private readonly List< Key > m_Keys;
        private readonly List< Table > m_Tables;
        private readonly List< String > m_TableAliases;

        private readonly List< ColumnReference > m_ColumnReferences;
        private readonly List< ColumnAlias > m_ColumnAliases;

        public CombinedKey Key;
        public List<ColumnAlias> GetUnderlying( ColumnReference underlying ) {
            return m_ColumnReferences.FindAll( assigned_alias => assigned_alias == underlying );
        }
        public Object GetExternalField( String column_alias ) {
            int column_index = m_ColumnReferences.FindIndex( assigned_alias => assigned_alias.Name == column_alias );

            return GetExternalField( m_ColumnAliases[ column_index ] );
        }

        public Object GetExternalField( ColumnAlias alias ) {
            Key key = m_Keys[ alias.TableIndex ];
            Object external_object = m_Tables[ alias.TableIndex ];
            return alias.Proxy.Eval( external_object );
        }

        public void Update( TableRow new_value, TableRow old_value ) { throw new NotImplementedException(); }
    }
}
